Skip to content

Conversation

@fabiankaegy
Copy link
Member

Previously, calling add_post_type_support() multiple times for the same post type and feature with array arguments would overwrite the previous arguments instead of merging them. This prevented plugins and themes from incrementally adding sub-properties to the same feature.

This changes the behavior to merge associative array arguments when the function is called multiple times with the same post type and feature combination.

Example usage:

add_post_type_support( 'page', 'editor', array(
    'default-mode' => 'template-locked',
) );

add_post_type_support( 'page', 'editor', array(
    'block-comments' => true,
) );

// Both properties are now preserved instead of only the last one.

The fix maintains full backwards compatibility by only merging when both the existing and new values are associative arrays. All other calling patterns continue to work as before.

Props @fabiankaegy, @Mamaduka, @t-hamano .

Trac ticket: https://core.trac.wordpress.org/ticket/64156#ticket


This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See GitHub Pull Requests for Code Review in the Core Handbook for more details.

Previously, calling `add_post_type_support()` multiple times for the same post type and feature with array arguments would overwrite the previous arguments instead of merging them. This prevented plugins and themes from incrementally adding sub-properties to the same feature.

This changes the behavior to merge associative array arguments when the function is called multiple times with the same post type and feature combination.

Example usage:

{{{
add_post_type_support( 'page', 'editor', array(
    'default-mode' => 'template-locked',
) );

add_post_type_support( 'page', 'editor', array(
    'block-comments' => true,
) );

// Both properties are now preserved instead of only the last one.
}}}

The fix maintains full backwards compatibility by only merging when both the existing and new values are associative arrays. All other calling patterns continue to work as before.

Props fabiankaegy, mamaduka, wildworks.
@github-actions
Copy link

github-actions bot commented Oct 27, 2025

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Core Committers: Use this line as a base for the props when committing in SVN:

Props fabiankaegy, wildworks, adamsilverstein, mamaduka, mindctrl, westonruter, johnjamesjacoby.

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@github-actions
Copy link

Test using WordPress Playground

The changes in this pull request can previewed and tested using a WordPress Playground instance.

WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser.

Some things to be aware of

  • The Plugin and Theme Directories cannot be accessed within Playground.
  • All changes will be lost when closing a tab with a Playground instance.
  • All changes will be lost when refreshing the page.
  • A fresh instance is created each time the link below is clicked.
  • Every time this pull request is updated, a new ZIP file containing all changes is created. If changes are not reflected in the Playground instance,
    it's possible that the most recent build failed, or has not completed. Check the list of workflow runs to be sure.

For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation.

Test this pull request with WordPress Playground.

Copy link
Member

@adamsilverstein adamsilverstein left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

@Mamaduka
Copy link
Member

IMO, the current behavior is broken, but given everything WP, would this be considered a breaking change?

cc @desrosj, @peterwilsoncc

Co-authored-by: Mamaduka <[email protected]>
if ( isset( $_wp_post_type_features[ $post_type ][ $feature ][0] )
&& is_array( $_wp_post_type_features[ $post_type ][ $feature ][0] )
&& isset( $args[0] )
&& is_array( $args[0] ) ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The is_array( $args[0] ) call here isn't checking whether the arg is an associative array. Shouldn't it also check if ! isset( $args[0][0] )?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or rather: ! array_is_list( $args[0] )

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additionally, what if someone tries to merge an empty array? Should that be allowed? So perhaps the new check here should be: ! array_is_list( $args[0] ) || empty( $args[0] )

$_wp_post_type_features[ $post_type ][ $feature ] = $args;
// Check if feature already exists with args and if both are associative arrays that should be merged.
if ( isset( $_wp_post_type_features[ $post_type ][ $feature ][0] )
&& is_array( $_wp_post_type_features[ $post_type ][ $feature ][0] )
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per below, should this check to make sure it is actually an associative array? I think it should add this check: ! array_is_list( $_wp_post_type_features[ $post_type ][ $feature ][0] )

@t-hamano
Copy link
Contributor

t-hamano commented Nov 7, 2025

The RC1 release is approaching, so we need to decide whether or not to ship this pull request. What do you think?

*
* @ticket 64156
*/
public function test_add_post_type_support_merges_array_arguments() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: Also add tests for theme features merging non-associative arrays.

@t-hamano
Copy link
Contributor

This pull request seems very robust, but my only concern is that it behaves differently from remove_post_type_support(). Currently, the remove_post_type_support() function shouldn't be able to remove only sub-features. What do you think?

@westonruter
Copy link
Member

This pull request seems very robust, but my only concern is that it behaves differently from remove_post_type_support(). Currently, the remove_post_type_support() function shouldn't be able to remove only sub-features. What do you think?

For completeness it seems that being able to remove sub-features via remove_post_type_support() makes sense. I don't know how common of a need it is, however. So this would entail adding a new $args param to remove_post_type_support() to match add_post_type_support():

function add_post_type_support( $post_type, $feature, ...$args )

So:

function remove_post_type_support( $post_type, $feature, ...$args )

But how would this be used? If $args[0] is an associative array, would it remove the supplied keys only if the values match? That would be the case if using array_diff(). But what if the user wants to remove the sub-feature regardless of the value?

@t-hamano
Copy link
Contributor

For completeness it seems that being able to remove sub-features via remove_post_type_support() makes sense. I don't know how common of a need it is,

I believe this need exists because the editor feature now has multiple sub-futures.

Consider a scenario where multiple sub-functions are registered as shown below, and you want to disable only the 'notes' sub-feature:

add_post_type_support( 'custom', 'editor', array(
    'default-mode' => 'template-locked',
    'notes'        => true,
) );

You will need to remove the existing main feature and then register the necessary sub-feature again, as follows.

remove_post_type_support( 'custom', 'editor' );
add_post_type_support( 'custom', 'editor', array(
    'default-mode' => 'template-locked',
) );

I don't think this concern will be a blocker here, but I wanted to share it just in case.

@JJJ
Copy link
Contributor

JJJ commented Nov 11, 2025

Consider also that $feature could be an array and $args gets set to all of them...

add_post_type_support( 'post', array( 'title', 'author' ), array( 'jjj' => 'cool' ) );

...and that remove_post_type_support() only supports removing a single $feature, so there already is not parity between them.


I think using $args as sub-features was not a great idea for the reasons highlighted by the discussion here, and that every sub-feature should be a $feature with a unique key with its own (or no) arguments.

Is it too late to create a new feature key instead?

@Mamaduka
Copy link
Member

Consider also that $feature could be an array and $args gets set to all of them

I think this was/is an "issue" even without this patch.

remove_post_type_support() only supports removing a single $feature, so there already is not parity between them.

Agreed, it makes sense to have feature parity here. We should also update post_type_supports and allow checking sub-features.

I think using $args as sub-features was not a great idea for the reasons highlighted by the discussion here, and that every sub-feature should be a $feature with a unique key with its own (or no) arguments.

Initially, it was implemented this way, but we changed it later, since these are the actual sub-features for editor. Notes won't work without editor support.

cc @swissspidy

* @since 3.0.0
* @since 5.3.0 Formalized the existing and already documented `...$args` parameter
* by adding it to the function signature.
* @since 6.9.0 Multiple calls to add support for the same feature with array
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As per my comment on the ticket, I'd say this is something for 7.0. It's an enhancement and could have unintended side effects for people who relied on the previous behavior, and we're about to publish RC1.

$this->assertTrue( $support['editor'][0]['block-comments'] );
$this->assertSame( 'test-value', $support['editor'][0]['another-option'] );

_unregister_post_type( 'foo' );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just noting that this code will never run if any of the above assertions fails. Best to move this to tear_down() or restructure the test to call this before the assertions.

);

$support = get_all_post_type_supports( 'foo' );
$this->assertIsArray( $support['editor'] );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The best practice in core is to add custom assertion messages (the last argument) when a test contains numerous assertions. Makes it much easier to debug a failing test and figuring out which assertion is actually failing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants